package com.nifostasky.main.thread.create;

import java.util.concurrent.*;

/**
 * 创建 : 将线程加入线程池;
 */
public class CreatePool {

    /**
     * **背景：**
     * <p>
     * 经常创建和销毁、使用量特别大的资源、比如并发情况下的线程、对性能影响很大。
     * <p>
     * **思路：**
     * <p>
     * 提前创建好多个线程，放入线程池中，使用时直接获取，使用完放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具。
     * <p>
     * **优点：**
     * <p>
     * 提高响应速度（减少了创建新线程的时间）
     * <p>
     * 降低资源消耗（重复利用线程池中线程，不需要每次都创建）
     * <p>
     * 便于线程管理
     */
    public static void main(String[] args) {
        //　　1）ArrayBlockingQueue：基于数组的先进先出队列，此队列创建时必须指定大小；
        //
        //　　2）LinkedBlockingQueue：基于链表的先进先出队列，如果创建时没有指定此队列大小，则默认为Integer.MAX_VALUE；
        //
        //　　3）synchronousQueue：这个队列比较特殊，它不会保存提交的任务，而是将直接新建一个线程来执行新来的任务。

        //Executors.newCachedThreadPool();        //创建一个缓冲池，缓冲池容量大小为Integer.MAX_VALUE
        //Executors.newSingleThreadExecutor();   //创建容量为1的缓冲池
        //Executors.newFixedThreadPool(int);    //创建固定容量大小的缓冲池

        //创建线程池: 核心线程5, 总线程10 , 最大超时时间 200ms , 等待队列5;
        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<Runnable>(5));

        for (int i = 0; i < 15; i++) {
            MyTask myTask = new MyTask(i);
            executor.execute(myTask);
            System.out.println("线程池中线程数目：" + executor.getPoolSize() + "，队列中等待执行的任务数目：" +
                    executor.getQueue().size() + "，已执行玩别的任务数目：" + executor.getCompletedTaskCount());
        }
        executor.shutdown();
    }
}
class MyTask implements Runnable {
    private int taskNum;

    public MyTask(int num) {
        this.taskNum = num;
    }

    @Override
    public void run() {
        System.out.println("正在执行task "+taskNum);
        try {
            Thread.currentThread().sleep(4000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("task "+taskNum+"执行完毕");
    }
}


/**
 * - **int corePoolSize**  =>  该线程池中**核心线程数最大值**
 *
 *   > **核心线程：**
 *   >
 *   > 线程池新建线程的时候，如果当前线程总数小于corePoolSize，则新建的是核心线程，如果超过corePoolSize，则新建的是非核心线程
 *   >
 *   > 核心线程默认情况下会一直存活在线程池中，即使这个核心线程啥也不干(闲置状态)。
 *   >
 *   > 如果指定ThreadPoolExecutor的allowCoreThreadTimeOut这个属性为true，那么核心线程如果不干活(闲置状态)的话，超过一定时间(时长下面参数决定)，就会被销毁掉
 *   >
 *   > 很好理解吧，正常情况下你不干活我也养你，因为我总有用到你的时候，但有时候特殊情况(比如我自己都养不起了)，那你不干活我就要把你干掉了
 *
 * - **int maximumPoolSize**
 *
 *   > 该线程池中**线程总数最大值**
 *   >
 *   > 线程总数 = 核心线程数 + 非核心线程数。核心线程在上面解释过了，这里说下非核心线程：
 *   >
 *   > 不是核心线程的线程(别激动，把刀放下...)，其实在上面解释过了
 *
 * - **long keepAliveTime**
 *
 *   > 该线程池中**非核心线程闲置超时时长**
 *   >
 *   > 一个非核心线程，如果不干活(闲置状态)的时长超过这个参数所设定的时长，就会被销毁掉
 *   >
 *   > 如果设置allowCoreThreadTimeOut = true，则会作用于核心线程
 *
 * - **TimeUnit unit**
 *
 *   > keepAliveTime的单位，TimeUnit是一个枚举类型，其包括：
 *   >
 *   > 1. NANOSECONDS ： 1微毫秒 = 1微秒 / 1000
 *   > 2. MICROSECONDS ： 1微秒 = 1毫秒 / 1000
 *   > 3. MILLISECONDS ： 1毫秒 = 1秒 /1000
 *   > 4. SECONDS ： 秒
 *   > 5. MINUTES ： 分
 *   > 6. HOURS ： 小时
 *   > 7. DAYS ： 天
 *
 * - **BlockingQueue<Runnable> workQueue**
 *
 *   > 该线程池中的任务队列：维护着等待执行的Runnable对象
 *   >
 *   > 当所有的核心线程都在干活时，新添加的任务会被添加到这个队列中等待处理，如果队列满了，则新建非核心线程执行任务
 *   >
 *   > 常用的workQueue类型：
 *   >
 *   > 1. **SynchronousQueue：**这个队列接收到任务的时候，会直接提交给线程处理，而不保留它，如果所有线程都在工作怎么办？那就新建一个线程来处理这个任务！所以为了保证不出现<线程数达到了maximumPoolSize而不能新建线程>的错误，使用这个类型队列的时候，maximumPoolSize一般指定成Integer.MAX_VALUE，即无限大
 *   > 2. **LinkedBlockingQueue：**这个队列接收到任务的时候，如果当前线程数小于核心线程数，则新建线程(核心线程)处理任务；如果当前线程数等于核心线程数，则进入队列等待。由于这个队列没有最大值限制，即所有超过核心线程数的任务都将被添加到队列中，这也就导致了maximumPoolSize的设定失效，因为总线程数永远不会超过corePoolSize
 *   > 3. **ArrayBlockingQueue：**可以限定队列的长度，接收到任务的时候，如果没有达到corePoolSize的值，则新建线程(核心线程)执行任务，如果达到了，则入队等候，如果队列已满，则新建线程(非核心线程)执行任务，又如果总线程数到了maximumPoolSize，并且队列也满了，则发生错误
 *   > 4. **DelayQueue：**队列内元素必须实现Delayed接口，这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时，首先先入队，只有达到了指定的延时时间，才会执行任务
 *
 * - **ThreadFactory threadFactory**
 *
 *   > 创建线程的方式，这是一个接口，你new他的时候需要实现他的`Thread newThread(Runnable r)`方法，一般用不上，**这是星期六，休息**
 *   >
 *   > 但我还是说一句吧(把枪放下...)
 *   >
 *   > 小伙伴应该知道AsyncTask是对线程池的封装吧？那就直接放一个AsyncTask新建线程池的threadFactory参数源码吧：
 *   >
 *   >
 *   >
 *   > ```cpp
 *   > new ThreadFactory() {
 *   >     private final AtomicInteger mCount = new AtomicInteger(1);
 *   >
 *   >     public Thread new Thread(Runnable r) {
 *   >         return new Thread(r,"AsyncTask #" + mCount.getAndIncrement());
 *   >     }
 *   > }
 *   > ```
 *   >
 *   > 这么简单？就给线程起了个名？！对啊，所以说这是星期六啊，别管他了，虽然我已经强迫你们看完了...
 *
 * - **RejectedExecutionHandler handler**
 *
 *   > 这玩意儿就是抛出异常专用的，比如上面提到的两个错误发生了，就会由这个handler抛出异常，你不指定他也有个默认的
 *   >
 *   > 抛异常能抛出什么花样来？所以这个星期天不管了，一边去，根本用不上
 */

/**
 * ### **3.线程池中的线程初始化**
 *
 * 　　默认情况下，创建线程池之后，线程池中是没有线程的，需要提交任务之后才会创建线程。
 *
 * 　　在实际中如果需要线程池创建之后立即创建线程，可以通过以下两个方法办到：
 *
 * - prestartCoreThread()：初始化一个核心线程；
 * - prestartAllCoreThreads()：初始化所有核心线程
 *
 * 　　下面是这2个方法的实现：
 *
 * ```java
 * public boolean prestartCoreThread() {
 *     return addIfUnderCorePoolSize(null); //注意传进去的参数是null
 * }
 *
 * public int prestartAllCoreThreads() {
 *     int n = 0;
 *     while (addIfUnderCorePoolSize(null))//注意传进去的参数是null
 *         ++n;
 *     return n;
 * }
 * ```
 *
 *  　注意上面传进去的参数是null，根据第2小节的分析可知如果传进去的参数为null，则最后执行线程会阻塞在getTask方法中的
 *
 * ```java
 * r = workQueue.take();
 * ```
 *
 *  　即等待任务队列中有任务。
 *
 * ### **4.任务缓存队列及排队策略**
 *
 * 　　在前面我们多次提到了任务缓存队列，即workQueue，它用来存放等待执行的任务。
 *
 * 　　workQueue的类型为BlockingQueue<Runnable>，通常可以取下面三种类型：
 *
 * 　　1）ArrayBlockingQueue：基于数组的先进先出队列，此队列创建时必须指定大小；
 *
 * 　　2）LinkedBlockingQueue：基于链表的先进先出队列，如果创建时没有指定此队列大小，则默认为Integer.MAX_VALUE；
 *
 * 　　3）synchronousQueue：这个队列比较特殊，它不会保存提交的任务，而是将直接新建一个线程来执行新来的任务。
 *
 * ### **5.任务拒绝策略**
 *
 * 　　当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize，如果还有任务到来就会采取任务拒绝策略，通常有以下四种策略：
 *
 * ```java
 * ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
 * ThreadPoolExecutor.DiscardPolicy：也是丢弃任务，但是不抛出异常。
 * ThreadPoolExecutor.DiscardOldestPolicy：丢弃队列最前面的任务，然后重新尝试执行任务（重复此过程）
 * ThreadPoolExecutor.CallerRunsPolicy：由调用线程处理该任务
 * ```
 *
 * ### **6.线程池的关闭**
 *
 * 　　ThreadPoolExecutor提供了两个方法，用于线程池的关闭，分别是shutdown()和shutdownNow()，其中：
 *
 * - shutdown()：不会立即终止线程池，而是要等所有任务缓存队列中的任务都执行完后才终止，但再也不会接受新的任务
 * - shutdownNow()：立即终止线程池，并尝试打断正在执行的任务，并且清空任务缓存队列，返回尚未执行的任务
 *
 * ### **7.线程池容量的动态调整**
 *
 * 　　ThreadPoolExecutor提供了动态调整线程池容量大小的方法：setCorePoolSize()和setMaximumPoolSize()，
 *
 * - setCorePoolSize：设置核心池大小
 * - setMaximumPoolSize：设置线程池最大能创建的线程数目大小
 *
 * 　　当上述参数从小变大时，ThreadPoolExecutor进行线程赋值，还可能立即创建新的线程来执行任务。
 */
